package sql

import (
	"LogisticsPort/model"
	"errors"
	"fmt"
	"github.com/gin-gonic/gin"
	"gorm.io/driver/mysql"
	"gorm.io/gorm"
	"gorm.io/gorm/schema"
	"net/http"
	"strconv"
	"strings"
)

//老服务器
/*const (
	name     = "root"
	password = "jym_20200427"
	//password = "jhj_0224"
	ip     = "49.232.199.223"
	port   = "3306"
	dbName = "logistics_port"
)*/

const (
	name     = "admin"
	password = "jym_20200427"
	//password = "jhj_0224"
	ip     = "124.220.61.29"
	port   = "3306"
	dbName = "logistics_port"
)

type HttpHandleFunc func(*gorm.DB) (*gorm.DB, interface{})
type DBHandleFunc func(db *gorm.DB) *gorm.DB

func HttpAdd(c *gin.Context, bean interface{}, handlerFunc DBHandleFunc) {
	HttpAddTable(c, "", bean, handlerFunc)
}

func HttpUpdates(c *gin.Context, bean interface{}, handlerFunc DBHandleFunc) {
	HttpUpdatesTable(c, "", bean, handlerFunc)
}

func HttpData(c *gin.Context, bean interface{}, dbHandle DBHandleFunc) {
	HttpDataTable(c, "", bean, dbHandle)
}

func HttpDataById(c *gin.Context, bean interface{}) {
	HttpDataTableById(c, "", bean)
}

func HttpList(c *gin.Context, bean interface{}, dbHandle DBHandleFunc) {
	HttpListTable(c, "", bean, dbHandle)
}

func HttpDelete(c *gin.Context, bean interface{}, dbHandle DBHandleFunc) {
	HttpDeleteTable(c, "", bean, dbHandle)
}

func HttpDataTableById(c *gin.Context, table string, bean interface{}) {
	HttpDataTable(c, table, bean, func(db *gorm.DB) *gorm.DB {
		if id := c.Query("id"); id == "" {
			db.Error = errors.New(ErrorParams)
		} else {
			db = db.Where("id = ?", id)
		}
		return db
	})
}

func HttpDeleteById(c *gin.Context, bean interface{}) {
	HttpDelete(c, bean, func(db *gorm.DB) *gorm.DB {
		if id := c.Query("id"); id == "" {
			db.Error = errors.New(ErrorParams)
		} else {
			db = db.Where("id = ?", id)
		}
		return db
	})
}

func HttpDeleteTableById(c *gin.Context, tableName string, bean interface{}) {
	HttpDeleteTable(c, tableName, bean, func(db *gorm.DB) *gorm.DB {
		if id := c.Query("id"); id == "" {
			db.Error = errors.New(ErrorParams)
		} else {
			db = db.Where("id = ?", id)
		}
		return db
	})
}

func HttpAddTable(c *gin.Context, tableName string, bean interface{}, handlerFunc DBHandleFunc) {
	HttpHandle(ADD, c, func(db *gorm.DB) (*gorm.DB, interface{}) {
		if err := c.Bind(bean); err != nil {
			fmt.Println("bind params Err:", err)
			db.Error = errors.New(ErrorParams)
			return db, nil
		}
		if handlerFunc != nil {
			db = handlerFunc(db)
		}
		if tableName != "" {
			db = db.Table(tableName)
		}
		db = db.Create(bean)
		if db.RowsAffected == 0 {
			if db.Error == nil {
				db.Error = errors.New(ErrorService)
			}
			return db, nil
		}
		return db, bean
	})
}

func HttpUpdatesTable(c *gin.Context, tableName string, bean interface{}, handlerFunc DBHandleFunc) {
	HttpHandle(UPDATE, c, func(db *gorm.DB) (*gorm.DB, interface{}) {
		if err := c.Bind(bean); err != nil {
			fmt.Println("bind params Err:", err)
			db.Error = errors.New(ErrorParams)
			return db, nil
		}
		if handlerFunc != nil {
			db = handlerFunc(db)
		}
		if tableName != "" {
			db = db.Table(tableName)
		}
		db = db.Save(bean)
		return db, bean
	})
}

func HttpDataTable(c *gin.Context, tableName string, bean interface{}, dbHandle DBHandleFunc) {
	HttpHandle(GET, c, func(db *gorm.DB) (*gorm.DB, interface{}) {
		if dbHandle == nil {
			db.Error = errors.New(ErrorParams)
			return db, nil
		}
		db = dbHandle(db)
		if db.Error != nil {
			return db, nil
		}
		if tableName != "" {
			db = db.Table(tableName)
		}
		db = db.First(bean)
		/*if db.RowsAffected == 0 {
			db.Error = errors.New(ErrorService)
			return db, nil
		}*/
		return db, bean
	})
}

func HttpDeleteTable(c *gin.Context, tableName string, bean interface{}, dbHandle DBHandleFunc) {
	HttpHandle(DELETE, c, func(db *gorm.DB) (*gorm.DB, interface{}) {
		if dbHandle != nil {
			db = dbHandle(db)
		} else {
			db.Error = errors.New(ErrorParams)
			return db, nil
		}
		if tableName != "" {
			db = db.Table(tableName)
		}

		/*db = db.First(&bean)
		if db.RowsAffected == 0 {
			db.Error = errors.New(ErrorUnExists)
			return db, nil
		}
		db.RowsAffected = 0*/
		db = db.Delete(&bean)
		if db.RowsAffected == 0 {
			db.Error = errors.New(ErrorUnExists)
			return db, nil
		}
		return db, bean
	})
}

func HttpListTable(c *gin.Context, tableName string, bean interface{}, dbHandle DBHandleFunc) {
	HttpHandle(GET, c, func(db *gorm.DB) (*gorm.DB, interface{}) {
		index := c.Query("pageIndex")
		size := c.Query("pageSize")
		pageIndex, indexErr := strconv.Atoi(index)
		pageSize, sizeError := strconv.Atoi(size)
		if indexErr == nil && sizeError == nil {
			db = db.Offset((pageIndex - 1) * pageSize).Limit(pageSize)
		}
		if dbHandle != nil {
			db = dbHandle(db)
		}
		if tableName != "" {
			db = db.Table(tableName)
		}
		db.Find(bean)
		return db, bean
	})
}

func update(db *gorm.DB, map1 map[string]float32) *gorm.DB {

	var formatMap = map[string]interface{}{}
	for s, f := range map1 {
		formatMap[s] = gorm.Expr(s+" - ?", f)
	}

	db = db.Updates(formatMap)
	return db
}

func ListPage(db *gorm.DB, c *gin.Context) *gorm.DB {
	index := c.Query("pageIndex")
	size := c.Query("pageSize")
	pageIndex, indexErr := strconv.Atoi(index)
	pageSize, sizeError := strconv.Atoi(size)
	if indexErr == nil && sizeError == nil {
		db = db.Offset((pageIndex - 1) * pageSize).Limit(pageSize)
	}
	return db
}

func HttpHandleBind(action string, c *gin.Context, bean interface{}, handlerFunc DBHandleFunc) {
	HttpHandle(action, c, func(db *gorm.DB) (*gorm.DB, interface{}) {
		if err := c.Bind(bean); err != nil {
			fmt.Println("bind params Err:", err)
			db.Error = errors.New(ErrorParams)
			return db, nil
		}
		handlerFunc(db)
		return db, bean
	})

}

func HttpHandle(action string, c *gin.Context, httpHandle HttpHandleFunc) {

	sqlPath := strings.Join([]string{name, ":", password, "@tcp(", ip, ":", port, ")/", dbName,
		"?charset=utf8&parseTime=True&loc=Local"}, "")
	fmt.Println(sqlPath)

	//只会验证格式是否正确，不会连接数据库
	db, err := gorm.Open(mysql.Open(sqlPath), &gorm.Config{
		NamingStrategy: schema.NamingStrategy{SingularTable: true},
	})
	if err != nil {
		fmt.Println("Open Database Err:", err)
		c.JSON(http.StatusInternalServerError, model.BaseJsonBean{Code: http.StatusInternalServerError, Message: ErrorService})
		return
	}

	sqlDB, err := db.DB()
	if err != nil {
		fmt.Println("Open Database Err:", err)
		c.JSON(http.StatusInternalServerError, model.BaseJsonBean{Code: http.StatusInternalServerError, Message: ErrorService})
		return
	}
	// 设置空闲连接池的最大连接数。
	sqlDB.SetMaxIdleConns(10)
	// 设置到数据库的最大打开连接数。
	sqlDB.SetMaxOpenConns(100)
	sqlDB.SetConnMaxLifetime(30)

	// 此时尝试连接数据库，会判断用户，密码，ip地址，端口是否正确
	err = sqlDB.Ping()
	if err != nil {
		fmt.Println("Connect Database Err:", err)
		c.JSON(http.StatusInternalServerError, model.BaseJsonBean{Code: http.StatusInternalServerError, Message: ErrorService})
		return
	}
	db, bean := httpHandle(db)
	err = sqlDB.Close()
	if err != nil { //数据库关闭失败
		fmt.Println("Close Database Err:", err)
		c.JSON(http.StatusInternalServerError, model.BaseJsonBean{Code: http.StatusInternalServerError, Message: ErrorService})
		return
	} else if db.Error != nil && db.Error.Error() == ErrorService {
		fmt.Println("Open Database Err:", db.Error)
		c.JSON(http.StatusInternalServerError, model.BaseJsonBean{Code: http.StatusInternalServerError, Message: ErrorService})
		return
	} else if db.Error != nil && db.Error.Error() == ErrorLogin {
		fmt.Println("Open Database Err:", db.Error)
		c.JSON(http.StatusUnauthorized, model.BaseJsonBean{Code: http.StatusUnauthorized, Message: ErrorLogin})
		return
	} else if db.Error != nil && (db.Error.Error() == "record not found" || db.Error.Error() == ErrorUnExists) {
		c.JSON(http.StatusOK, &model.BaseJsonBean{Code: http.StatusOK, Message: ErrorUnExists})
		return
	} else if db.Error != nil && (db.Error.Error() == "data error,data must be greater than 0") {
		c.JSON(http.StatusBadRequest, &model.BaseJsonBean{Code: http.StatusBadRequest, Message: ErrorData})
		return
	} else if db.Error != nil {
		fmt.Println("Request params Err:  ", db.Error)
		//Error 1146: Table 'logistics_port.tax_fee' doesn't exist
		//Error 1048: Column 'deleted_at' cannot be null
		//WHERE conditions required 主键错误，修改数据没上传主键或主键大小写错误
		c.JSON(http.StatusBadRequest, model.BaseJsonBean{Code: http.StatusBadRequest, Message: db.Error.Error()})
		return
	}

	c.JSON(http.StatusOK, &model.BaseJsonBean{
		Code: http.StatusOK, Message: action + "成功", Data: bean,
	})
}

// PostParamsBind /** 数据绑定，新增、编辑时，对新增数据进行解析
func PostParamsBind(c *gin.Context, db *gorm.DB, product interface{}) gorm.DB {
	if err := c.Bind(product); err != nil {
		fmt.Println("bind params Err:", err)
		db.Error = errors.New(ErrorParams)
	}
	return *db
}
